﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MarioArena.Multiplayer.TCP;
using MarioArena.World;
using MarioArena.Multiplayer;
using System.IO;

namespace MarioArena.Launcher
{
    public partial class LauncherForm : Form
    {
        private bool mReady = false;
        private bool mIsLaunchingGame = false;

        public LauncherForm()
        {
            InitializeComponent();
            AddEventHandlers();
            //Load default connection values from Settings
            tbName.Text = Settings.Name;
            tbIp.Text = Settings.IPAddress;
            tbPort.Text = Settings.Port.ToString();
            //Disable name input if user joined from webservice
            if (Settings.LaunchedWithArguments)
                tbName.Enabled = false;
            Settings.ValidateApplicationData();
        }

        
        
        /// <summary>
        /// Processes arguments, and attempts to automatically connect with the server given by these arguments.
        /// </summary>
        /// <param name="arguments"></param>
        /// <returns></returns>
        public bool ProcessArguments(String[] arguments)
        {
            int amountOfArguments = arguments.Length;
            if (amountOfArguments == 4)
            {
                try
                {
                    string ip = arguments[0];
                    int port = Convert.ToInt32(arguments[1]);
                    string playername = arguments[2];
                    string playerid = arguments[3];
                    Settings.IPAddress = ip;
                    Settings.Port = port;
                    Settings.Name = playername;
                    Settings.WebID = playerid;
                    Settings.LaunchedWithArguments = true;
                    tbIp.Text = ip;
                    tbName.Text = playername;
                    tbPort.Text = arguments[1];
                    return true;
                }
                catch (Exception e) { Console.WriteLine("Could not process arguments: " + e); return false; }
            }
            return false;
        }

        /// <summary>
        /// Called when the form has finished loading and is shown for the first time.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Launcher_Shown(object sender, EventArgs e)
        {
            this.Refresh();
            //If arguments were passed, immediately attempt to establish a connection after the form is shown.
            if (Settings.LaunchedWithArguments)
            {
                AttemptConnection();
            }
        }

        private void AddEventHandlers()
        {
            this.lbPlayers.DrawItem += new DrawItemEventHandler(this.lbPlayers_DrawItem);
            this.Shown += new EventHandler(Launcher_Shown);
        }

        

        private void lbPlayers_DrawItem(Object sender, DrawItemEventArgs e)
        {
            string text = lbPlayers.Items[e.Index].ToString();
            e.DrawBackground();
            Graphics g = e.Graphics;

            if (lbPlayers.Items[e.Index] is Opponent) // Item is an Opponent object
            {
                if ((lbPlayers.Items[e.Index] as Opponent).IsReadyInLobby)
                {
                    g.FillRectangle(new SolidBrush(Color.LightSeaGreen), e.Bounds); // Draw opponent as ready
                }
                else
                {
                    g.FillRectangle(new SolidBrush(Color.LightSalmon), e.Bounds); // Draw opponent as NOT ready
                }
            }
            else // Item is the player himself (string object)
            {
                if (Player.Instance.IsReadyInLobby)
                {
                    g.FillRectangle(new SolidBrush(Color.LightSeaGreen), e.Bounds); // Draw player as ready
                }
                else
                {
                    g.FillRectangle(new SolidBrush(Color.LightSalmon), e.Bounds); // Draw player as NOT ready
                }
            }
            Font font = new Font(e.Font, FontStyle.Bold);
            g.DrawString(text, font, new SolidBrush(Color.White), e.Bounds); // Draw the text

            e.DrawFocusRectangle();
        }

        /// <summary>
        /// Occurs when the user click the Connect button. Attempts to establish a connection with a server.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonConnect_Click(object sender, EventArgs e)
        {
            if (ValidateSettings())
                ProcessFieldsForConnection();
        }

        /// <summary>
        /// Occurs when the user clicks the Ready button. Sends a signal to server that this client is ready to start the game.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonReady_Click(object sender, EventArgs e)
        {
            try
            {
                string command = "5_";
                if (mReady == false) // Previous state was NOT READY
                {
                    mReady = true; // New state is READY
                    command += "1"; // Send READY command
                }
                else if (mReady == true)
                {
                    mReady = false;
                    command += "0"; // Send NOT READY command
                }
                TCPConnection.Instance.Send(command);
            }
            catch (Exception)
            {
                MessageBox.Show("Connection lost. Could not send a Ready command.");
            }

        }

        /// <summary>
        /// Retrieves user input and starts stores all of it in Settings. After that the AttemptConnection() function is called.
        /// </summary>
        private void ProcessFieldsForConnection()
        {
            string name;
            string ip;
            int port;
            try
            {
                name = tbName.Text;
                Settings.Name = name;
                ip = tbIp.Text;
                if(ip.ToLower().Equals("localhost"))
                    ip = "127.0.0.1";
                Settings.IPAddress = ip;
                port = Convert.ToInt32(tbPort.Text);
                Settings.Port = port;
                
                //new Thread(new ThreadStart(AttemptConnection)).Start(); //Attempt connection
                AttemptConnection();
            }
            catch (Exception e) { MessageBox.Show("Port is not valid!"); Console.WriteLine("Error starting UDP: " + e); }
        }

        /// <summary>
        /// Attempts to connect to the server, using the Settings as connection data.
        /// </summary>
        private void AttemptConnection()
        {
            buttonConnect.Enabled = false;
            bool connectionFailed = false;

            //using (new LoadingPopup(this, "Connecting...")) //SHOW LOADING SCREEN
            //{
            labelLoading.Text = "Connecting...";
            Refresh();
            if (TCPConnection.Instance.StartClient(Settings.IPAddress, Settings.Port) == true) // Connection succesful
            {
                Console.WriteLine("TCP connection established.");
                UDPConnection.Instance.Initialize(); // Initialize the UDP connection and send the UDP port to the server
                lbPlayers.Items.Add(tbName.Text);
                try
                {
                    TCPConnection.Instance.Send("2_" + tbName.Text + "_" + Settings.WebID);
                    TCPConnection.Instance.Send("99_" + UDPConnection.Instance.LocalPort.ToString());
                }
                catch (Exception)
                {
                    MessageBox.Show("Connection error. Could not send the name to the server.");
                }
                ShowLobby();
            }
            else
            {
                connectionFailed = true;
            }
            buttonConnect.Enabled = true;
            //}
            if (connectionFailed)
            {
                labelLoading.Text = "Connection failed.";
                MessageBox.Show("Cannot connect to server.");
            }
            labelLoading.Text = "";
        }

        /// <summary>
        /// Shows the lobby menu.
        /// </summary>
        private void ShowLobby()
        {
            //this.panelConnect.Visible = false;
            this.panelConnect.Enabled = false;
            this.panelLobby.Enabled = true;
        }

        /// <summary>
        /// Show the main menu.
        /// </summary>
        public void ReturnToConnectMenu()
        {
            this.panelConnect.Enabled = true;
            //For registered user: disable name input after returning to connect menu
            if (Settings.WebID != "")
                tbName.Enabled = false;
            
            this.panelLobby.Enabled = false;
            this.lbPlayers.Items.Clear();
        }

        /// <summary>
        /// Launches the game, closing the launcher in the process.
        /// </summary>
        public void LaunchGame()
        {
            this.mIsLaunchingGame = true;
            this.DialogResult = DialogResult.OK;
            this.Close();
        }

        /// <summary>
        /// Validates all input fields and notifies the user of faulty input.
        /// </summary>
        /// <returns></returns>
        private bool ValidateSettings()
        {
            if (this.tbName.Text.Length < 3)
            {
                MessageBox.Show("Please use at least three characters for your username.");
                return false;
            }
            if(!IsValidIp(tbIp.Text))
                return false;
            if (!IsValidPort(tbPort.Text))
                return false;
            return true;
        }

        /// <summary>
        /// Checks if an ip is valid (only numbers and dots)
        /// </summary>
        /// <param name="ip">Ip address string to check</param>
        /// <returns></returns>
        private bool IsValidIp(string ip)
        {
            if (ip.ToLower().Equals("localhost"))
                return true;
            char[] validChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' };
            for (int i = 0; i < ip.Length; i++)
            {
                if (!validChars.Contains(ip[i]))
                {
                    MessageBox.Show("Invalid IP. ' " + ip[i] + " ' is not a valid character.");
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// Checks if a port is valid (only numbers)
        /// </summary>
        /// <param name="port">Port to check</param>
        /// <returns></returns>
        private bool IsValidPort(string port)
        {
            char[] validChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
            for (int i = 0; i < port.Length; i++)
            {
                if (!validChars.Contains(port[i]))
                {
                    MessageBox.Show("Invalid port. ' " + port[i] + " ' is not a valid character.");
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Returns the listbox containing all connected players.
        /// </summary>
        /// <returns></returns>
        public ListBox GetPlayerListBox()
        {
            return this.lbPlayers;
        }

        /// <summary>
        /// Returns the ready button.
        /// </summary>
        /// <returns></returns>
        public Button GetReadyButton()
        {
            return this.buttonReady;
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            if (!this.mIsLaunchingGame) // Form is closed by the player, not because the game is launched
                TCPConnection.Instance.Connected = false; // Stop the TCP connection to prevent the thread, and thus the application, from continuing
        }
        private void buttonDisconnect_Click(object sender, EventArgs e)
        {
            if (!this.mIsLaunchingGame) //If game isnt launching
            {
                TCPConnection.Instance.Disconnect();
            }
        }

        
    }
}
